﻿using Framework.Infrastructure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Framework.Repository.Extensions
{
    internal class QueryableSearcher<T>
    {
        protected IEnumerable<QueryFilter> Items { get; set; }

        protected IQueryable<T> Table { get; set; }

        public QueryableSearcher(IQueryable<T> table, IEnumerable<QueryFilter> items)
        {
            Table = table;
            Items = items;
        }

        public IQueryable<T> Search()
        {
            ParameterExpression param = Expression.Parameter(typeof(T), "c");
            var body = GetExpressoininBody(param, Items);
            var expression = Expression.Lambda<Func<T, bool>>(body, param);
            return Table.Where(expression);
        }

        private Expression GetExpressoininBody(ParameterExpression param, IEnumerable<QueryFilter> Items)
        {
            Expression expression = null;

            //找出没有加分组的项 这些都用And连接
            var andList = Items.Where(c => string.IsNullOrEmpty(c.OrGroup)).ToList();
            if (andList.Count() != 0)
            {
                foreach (var item in andList)
                {
                    Expression left = GetExpressionByCondition(param, item);
                    expression = expression == null ? left : Expression.AndAlso(left, expression);
                }
            }

            //找出加组的 组内部以or组合 组与组用and
            var orGroupByList = Items.Where(c => !string.IsNullOrEmpty(c.OrGroup)).GroupBy(c => c.OrGroup);
            foreach (IGrouping<string, QueryFilter> group in orGroupByList)
            {
                if (group.Count() != 0)
                {
                    Expression orGroupByExpAnd = null;
                    foreach (var item in group)//o=>o.id==2||o.name=="3"
                    {
                        Expression left = GetExpressionByCondition(param, item);
                        orGroupByExpAnd = orGroupByExpAnd == null ? left : Expression.OrElse(left, orGroupByExpAnd);
                    }
                    expression = expression == null ? orGroupByExpAnd : Expression.AndAlso(orGroupByExpAnd, expression); ;
                }
            }
            return expression;
        }

        /// <summary>
        /// 根据规则 生成表达式树
        /// </summary>
        /// <param name="param"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        private Expression GetExpressionByCondition(ParameterExpression param, QueryFilter item)
        {
            LambdaExpression exp = GetPropertyLambdaExpression(item, param);//得到属性 如果c.name

            var constant = ChangeTypeToExpression(item, exp.Body.Type);//得到常量 例如 "wlf"
            var selectexp = ExpressionDict[item.Operator];//选择连接方法 比如是等于还是like
            return selectexp(exp.Body, constant); //通过方法连接属性以及参数 得到c.name=="wlf"的表达式
        }

        private Expression ChangeTypeToExpression(QueryFilter item, Type type)
        {
            #region 数组
            if (item.Operator == QueryMethod.StdIn || item.Operator == QueryMethod.StdNotIn)
            {
                var arr = (item.FieldValue as Array);
                var expList = new List<Expression>();
                if (arr != null)
                {
                    for (int i = 0; i < arr.Length; i++)
                    {
                        var newvalue = ChangeType(arr.GetValue(i), type);
                        expList.Add(Expression.Constant(newvalue, type));
                    }
                    return Expression.NewArrayInit(type, expList);
                }
            }
            #endregion

            var elementType = Common.GetUnNullableType(type);
            if (elementType.IsEnum)
            {
                var enumvalue = Enum.Parse(elementType, item.FieldValue.ToString());
                return Expression.Constant(enumvalue, type);
            }
            if (elementType.Name == "Guid")
            {
                var value = Guid.Parse(item.FieldValue.ToString());
                return Expression.Constant(value, type);
            }
            else
            {
                var value = Convert.ChangeType(item.FieldValue, elementType);
                return Expression.Constant(value, type);
            }
        }

        /// <summary>
        /// 类型转换，支持非空类型与可空类型之间的转换
        /// </summary>
        /// <param name="value"></param>
        /// <param name="conversionType"></param>
        /// <returns></returns>
        public static object ChangeType(object value, Type conversionType)
        {
            if (value == null) return null;
            return Convert.ChangeType(value, Common.GetUnNullableType(conversionType));
        }

        private LambdaExpression GetPropertyLambdaExpression(QueryFilter item, ParameterExpression param)
        {
            //获取每级属性如c.StuClass.ClassName
            var props = item.FieldName.Split('.');
            var typeOfProp = typeof(T);
            Expression exppro = param;
            for (int i = 0; i < props.Length; i++)
            {
                PropertyInfo property = typeOfProp.GetProperty(props[i]);
                if (property == null) return null;
                typeOfProp = property.PropertyType;
                exppro = Expression.Property(exppro, property);
            }
            return Expression.Lambda(exppro, param);
        }

        #region QueryMethod 操作方法
        private static readonly Dictionary<QueryMethod, Func<Expression, Expression, Expression>> ExpressionDict = new Dictionary<QueryMethod, Func<Expression, Expression, Expression>>
                {
                    { QueryMethod.Equal,(left, right) => { return Expression.Equal(left, right); }},
                    { QueryMethod.GreaterThan,(left, right) => { return Expression.GreaterThan(left, right); }},
                    { QueryMethod.GreaterThanOrEqual,(left, right) => { return Expression.GreaterThanOrEqual(left, right); }},
                    { QueryMethod.LessThan,(left, right) => { return Expression.LessThan(left, right); }},
                    { QueryMethod.LessThanOrEqual,(left, right) => { return Expression.LessThanOrEqual(left, right); }},
                    { QueryMethod.Contains,(left, right) =>{ if (left.Type != typeof (string)) return null; return Expression.Call(left, typeof (string).GetMethod("Contains"), right); }},
                    { QueryMethod.StdIn,(left, right) =>{
                                if (!right.Type.IsArray) return null;
                                //调用Enumerable.Contains扩展方法
                                MethodCallExpression resultExp = Expression.Call(typeof (Enumerable),"Contains",new[] {left.Type},right,left);return resultExp;}}, 
                    { QueryMethod.StdNotIn,(left, right) =>{
                                if (!right.Type.IsArray) return null;
                                //调用Enumerable.Contains扩展方法
                                MethodCallExpression resultExp = Expression.Call(typeof (Enumerable),"Contains", new[] {left.Type},right,left); var notrxp=  Expression.Not(resultExp); return notrxp; }},
                    { QueryMethod.NotEqual,(left, right) => { return Expression.NotEqual(left, right); }},
                    { QueryMethod.StartsWith,(left, right) =>{
                                if (left.Type != typeof (string)) return null;
                                return Expression.Call(left, typeof (string).GetMethod("StartsWith", new[] {typeof (string)}), right);}},
                    { QueryMethod.EndsWith,(left, right) =>{ if (left.Type != typeof (string)) return null;return Expression.Call(left, typeof (string).GetMethod("EndsWith", new[] {typeof (string)}), right);}}
                };
        #endregion

    }
}
